﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Caduceu.Estudos.Entidades;
using Caduceu.Estudos.DAL;

namespace Caduceu.Estudos.LogicaNegocio
{
    // As classes Controller podem ser entendidas como rotinas do sistema. As classes Controller
    // normalmente exigirão apenas alguns parâmetros de entrada para executar seu trabalho (normalmente,
    // esses parâmetros são os mesmos que serão mostrados na forma de campos pela camada de apresentação).
    // As classes Controller ficarão encarregadas de coordenar a ação de todos os demais objetos, e de
    // gerenciar os objetos compartilhados entre eles (como transações de BD), visando atingir um objetivo.
    // Podemos entender, grosso modo, que uma classe Controller 'executa um diagrama de seqüências da UML',
    // ou então que 'executa um diagrama de colaborações da UML'

    public class ContabilController
    {
        protected Dictionary<string, ContaContabil> dictContas = null;

        protected void InicializarListaContas()
        {
            if (dictContas == null)
            {
                using (ContaContabilDao dao = new ContaContabilDao(new DbSession()))
                {
                    dictContas = ContaContabilService.MontaDicionarioContasPorNumero(dao.GetContasContabeis());
                }
            }
        }

        public void RegistrarLancamentos(IList lancamentos)
        {
            if (lancamentos.Count == 0)
                throw new ArgumentException("A lista de lançamentos está vazia", "lancamentos");

            InicializarListaContas();

            DbSession session = new DbSession();
            session.BeginTransaction();

            LancamentoDao lancamentoDao = null;
            MovimentoContaDao movContaDao = null;

            try
            {
                lancamentoDao = new LancamentoDao(session);
                movContaDao = new MovimentoContaDao(session);

                try
                {
                    foreach (Lancamento lancamento in lancamentos)
                    {
                        if (lancamento == null)
                            throw new Exception("Um dos objetos da lista não é um Lancamento, favor verificar a lista");

                        // verificar conta débito do lançamento 
                        ContaContabil contaDebito = null;
                        if (!dictContas.TryGetValue(lancamento.NumeroContaDebito, out contaDebito))
                            throw new InvalidOperationException("A Conta Débito especificada não foi encontrada");

                        // verificar conta crédito do lançamento 
                        ContaContabil contaCredito = null;
                        if (!dictContas.TryGetValue(lancamento.NumeroContaCredito, out contaCredito))
                            throw new InvalidOperationException("A Conta Crédito especificada não foi encontrada");

                        // criar movimentos com base nas contas encontradas
                        MovimentoConta movimentoDebito = new MovimentoConta();
                        movimentoDebito.ContaMovimento = contaDebito;
                        movimentoDebito.TipoMovimento = 'D';
                        movimentoDebito.Valor = lancamento.Valor;

                        MovimentoConta movimentoCredito = new MovimentoConta();
                        movimentoCredito.ContaMovimento = contaCredito;
                        movimentoCredito.TipoMovimento = 'C';
                        movimentoCredito.Valor = lancamento.Valor;

                        // persiste os objetos na base
                        lancamentoDao.SalvarLancamento(lancamento);
                        movContaDao.SalvarMovimento(movimentoDebito);
                        movContaDao.SalvarMovimento(movimentoCredito);
                    }

                    session.CommitTransaction();
                }
                catch
                {
                    session.RollbackTransaction();
                    throw;
                }
            }
            finally
            {
                if (lancamentoDao != null) lancamentoDao.Dispose();
                if (movContaDao != null) movContaDao.Dispose();
            }
        }

        public void RegistrarLancamento(Lancamento lancamento)
        {
            RegistrarLancamentos(new Lancamento[] { lancamento });
        }

        public void FecharContabil()
        {
            // Objetivo: testar a possibilidade de se usar queries personalizadas, sem o mapeamento O/R, mas
            // usando os mecanismos de conexão do nhibernate

            DbSession session = new DbSession();

            MovimentoContaDao movContaDao = null;
            ContaContabilDao contaDao = null;
            SaldoContaDao saldoContaDao = null;

            try
            {
                session.BeginTransaction();

                movContaDao = new MovimentoContaDao(session);

                // este método usa uma query normal, mas utilizando a mesma conexão do nhibernate
                Dictionary<int, decimal> movimentosPorConta = movContaDao.LevantarMovimentosTotais();

                if (movimentosPorConta.Count > 0)
                {
                    SaldoConta saldo;

                    contaDao = new ContaContabilDao(session);
                    saldoContaDao = new SaldoContaDao(session);

                    IList<ContaContabil> contas = contaDao.GetContasContabeis();
                    Dictionary<int, ContaContabil> contasPorId = new Dictionary<int, ContaContabil>();

                    foreach (ContaContabil conta in contas)
                        contasPorId.Add(conta.Id, conta);
                                        

                    foreach (int idConta in movimentosPorConta.Keys)
                    {
                        saldo = new SaldoConta();
                        saldo.ContaMovimento = contasPorId[idConta];
                        saldo.Valor = movimentosPorConta[idConta];

                        saldoContaDao.SalvarSaldoConta(saldo);
                    }
                }

                session.CommitTransaction();
            }
            catch (Exception ex)
            {
                session.RollbackTransaction();
                throw;
            }
            finally
            {
                if (saldoContaDao != null) saldoContaDao.Dispose();
                if (contaDao != null) contaDao.Dispose();
                if (movContaDao != null) movContaDao.Dispose();
            }
        }
    }
}
